#include "utilc_hash_map.h"

static void util_hash_map_number_destory_fun(void* p) {
}

static int util_hash_map_number_compair_fun(void *first, void *second) {
    uint64_t x = (uint64_t)first;
    uint64_t y = (uint64_t)second;
    if(x < y)
        return -1;
    if(x > y)
        return 1;
    return 0;
}

static int util_hash_map_number_hash_fun(void* p) {
    return (int)p;
}

//////////////////////////////////////////////////////////////////////////

static void util_hash_map_string_destory_fun(void* p) {
    util_string_unref((util_string_t*)p);
}

static int util_hash_map_string_compair_fun(void *first, void *second) {
    return util_string_compare((util_string_t*)first, (util_string_t*)second);
}

static int util_hash_map_string_hash_fun(void* p) {
    util_string_t* val = (util_string_t*)p;
    int len = (int)util_string_size(val);
    int i,hash=0;
    for(i=0; i<len; i++) {
        hash = hash*31 + util_string_at(val, i);
    }
    return hash;
}

//////////////////////////////////////////////////////////////////////////

util_hash_map_t* util_hash_map_create(DESTORY_FUN fun_destory_key, DESTORY_FUN fun_destory_value, 
                                      COMPAIR_FUN fun_key_compare, COMPAIR_FUN fun_value_compare, 
                                      HASH_FUN fun_hash) {
    uint32_t i;
    SAFE_MALLOC(util_hash_map_t, ret);
    ret->bucket_size       = sizeof(ret->buckets)/sizeof(util_hash_map_bucket_t);
    ret->fun_destory_key   = fun_destory_key;
    ret->fun_destory_value = fun_destory_value;
    ret->fun_key_compare   = fun_key_compare;
    ret->fun_value_compare = fun_value_compare;
    ret->fun_hash          = fun_hash;
    for(i=0; i<ret->bucket_size; i++) {
        ret->buckets[i].bucket.bucket_list = (util_pair_linked_list_t*)util_linked_list_create();
    }
    return ret;
}

number_hash_map_t* number_hash_map_create(DESTORY_FUN fun_destory_value, COMPAIR_FUN fun_value_compare) {
    uint32_t i;
    SAFE_MALLOC(number_hash_map_t, ret);
    ret->bucket_size       = sizeof(ret->buckets)/sizeof(number_hash_map_bucket_t);
    ret->fun_destory_key   = util_hash_map_number_destory_fun;
    ret->fun_destory_value = fun_destory_value;
    ret->fun_key_compare   = util_hash_map_number_compair_fun;
    ret->fun_value_compare = fun_value_compare;
    ret->fun_hash          = util_hash_map_number_hash_fun;
    for(i=0; i<ret->bucket_size; i++) {
        ret->buckets[i].bucket.bucket_list = (number_pair_linked_list_t*)util_linked_list_create();
    }
    return ret;
}

string_hash_map_t* string_hash_map_create(DESTORY_FUN fun_destory_value, COMPAIR_FUN fun_value_compare) {
    uint32_t i;
    SAFE_MALLOC(string_hash_map_t, ret);
    ret->bucket_size       = sizeof(ret->buckets)/sizeof(string_hash_map_bucket_t);
    ret->fun_destory_key   = util_hash_map_string_destory_fun;
    ret->fun_destory_value = fun_destory_value;
    ret->fun_key_compare   = util_hash_map_string_compair_fun;
    ret->fun_value_compare = fun_value_compare;
    ret->fun_hash          = util_hash_map_string_hash_fun;
    for(i=0; i<ret->bucket_size; i++) {
        ret->buckets[i].bucket.bucket_list = (string_pair_linked_list_t*)util_linked_list_create();
    }
    return ret;
}

void util_hash_map_clear(util_hash_map_t *map) {
    uint32_t i;
    for(i=0; i<map->bucket_size; i++) {
        if(map->buckets[i].size == 0)
            continue;
        util_linked_list_clear((util_linked_list_t*)map->buckets[i].bucket.bucket_list);
        map->buckets[i].size = 0;
    }
    map->size = 0;
}

void util_hash_map_destory(util_hash_map_t *map) {
    uint32_t i;
    for(i=0; i<map->bucket_size; i++) {
        util_linked_list_unref((util_linked_list_t*)map->buckets[i].bucket.bucket_list);
    }
    free(map);
}

void util_hash_map_ref(util_hash_map_t *map) {
    map->ref++;
}

void util_hash_map_unref(util_hash_map_t *map) {
    if(map->ref==0) {
        util_hash_map_destory(map);
        return;
    }
    map->ref--;
}

uint32_t util_hash_map_size(util_hash_map_t *map) {
    return map->size;
}

bool util_hash_map_empty(util_hash_map_t *map) {
    if(map->size==0)
        return true;
    return false;
}

bool util_hash_map_contains_key(util_hash_map_t *map, void* key) {
    int hash_code = map->fun_hash(key);
    util_pair_linked_list_t *bucket_list;
    util_pair_linked_list_node_t *tmp;
    hash_code %= map->bucket_size;
    if(map->buckets[hash_code].size <= 0)
        return false;
    bucket_list = map->buckets[hash_code].bucket.bucket_list;
    if(util_linked_list_size((util_linked_list_t*)bucket_list)==0)
        return false;
    for(tmp = bucket_list->head; tmp != NULL; tmp=tmp->next) {
        if(map->fun_key_compare(tmp->first, key) == 0) {
            return true;
        }
    }
    return false;
}

bool util_hash_map_contains_value(util_hash_map_t *map, void *val) {
    uint32_t i;
    util_pair_linked_list_t *bucket_list;
    util_pair_linked_list_node_t *tmp;
    for(i=0; i<map->bucket_size; i++) {
        if(map->buckets[i].size <= 0)
            continue;
        bucket_list = map->buckets[i].bucket.bucket_list;
        if(util_linked_list_size((util_linked_list_t*)bucket_list)==0)
            continue;
        for(tmp = bucket_list->head; tmp != NULL; tmp=tmp->next) {
            if(map->fun_value_compare(tmp->first, val) == 0) {
                return true;
            }
        }
    }
    return false;
}

void* util_hash_map_get(util_hash_map_t *map, void* key) {
    int hash_code = map->fun_hash(key);
    util_pair_linked_list_t *bucket_list;
    util_pair_linked_list_node_t *tmp;
    hash_code %= map->bucket_size;
    if(map->buckets[hash_code].size <= 0)
        return NULL;
    bucket_list = map->buckets[hash_code].bucket.bucket_list;
    if(util_linked_list_size((util_linked_list_t*)bucket_list)==0)
        return NULL;
    for(tmp = bucket_list->head; tmp != NULL; tmp=tmp->next) {
        if(map->fun_key_compare(tmp->first, key) == 0) {
            return tmp->second;
        }
    }
    return NULL;
}

void util_hash_map_put(util_hash_map_t *map, void *key, void *val) {
    util_pair_linked_list_t *bucket_list;
    util_pair_linked_list_node_t *tmp;
    util_pair_linked_list_node_t *new_node;

    int hash_code = map->fun_hash(key);
    hash_code %= map->bucket_size;
    bucket_list = map->buckets[hash_code].bucket.bucket_list;
    for(tmp = bucket_list->head; tmp != NULL; tmp=tmp->next) {
        if(map->fun_key_compare(tmp->first, key) == 0) {
            map->fun_destory_value(tmp->second);
            tmp->second = val;
            return;
        }
    }
    new_node = (util_pair_linked_list_node_t*)calloc(1, sizeof(util_pair_linked_list_node_t));
    new_node->first = key;
    new_node->second = val;
    new_node->destory_fun = map->fun_destory_value;
    util_linked_list_push_back((util_linked_list_t*)bucket_list, (util_linked_list_node_t*)new_node);

    map->buckets[hash_code].size++;
    map->size++;
}

void util_hash_map_remove(util_hash_map_t *map, void *key) {
    int hash_code = map->fun_hash(key);
    util_pair_linked_list_t *bucket_list;
    util_pair_linked_list_node_t *tmp;
    hash_code %= map->bucket_size;
    bucket_list = map->buckets[hash_code].bucket.bucket_list;
    for(tmp = bucket_list->head; tmp != NULL; tmp=tmp->next) {
        if(map->fun_key_compare(tmp->first, key) == 0) {
            map->fun_destory_key(tmp->first);
            map->fun_destory_value(tmp->second);
            util_linked_list_remove((util_linked_list_t*)bucket_list, (util_linked_list_node_t*)tmp);
            map->buckets[hash_code].size--;
            map->size--;
            return;
        }
    }
}

util_hash_map_iter_t util_hash_map_begin(util_hash_map_t *map) {
    uint32_t i;
    util_pair_linked_list_t *bucket_list;
    util_hash_map_iter_t tmp = {-1, map, NULL};
    for(i=0; i<map->bucket_size; i++) {
        if(map->buckets[i].size <= 0)
            continue;
        bucket_list = map->buckets[i].bucket.bucket_list;
        if(util_linked_list_size((util_linked_list_t*)bucket_list)==0)
            continue;
        tmp.bucket_num = i;
        tmp.bucket_list_node = bucket_list->head;
        return tmp;
    }
    return tmp;
}

util_hash_map_iter_t util_hash_map_iter_next(util_hash_map_iter_t iter) {
    util_hash_map_iter_t ret = {-1, iter.map, NULL};
    uint32_t i;
    util_pair_linked_list_t *bucket_list;
    if(iter.bucket_list_node->next != NULL) {
        ret.bucket_num = iter.bucket_num;
        ret.bucket_list_node = iter.bucket_list_node->next;
        return ret;
    }
    for(i=iter.bucket_num+1; i<iter.map->bucket_size; i++) {
        if(iter.map->buckets[i].size <= 0)
            continue;
        bucket_list = iter.map->buckets[i].bucket.bucket_list;
        if(util_linked_list_size((util_linked_list_t*)bucket_list)==0)
            continue;
        ret.bucket_num = i;
        ret.bucket_list_node = bucket_list->head;
        return ret;
    }
    return ret;
}

util_hash_map_iter_t util_hash_map_iter_erase(util_hash_map_iter_t iter) {
    util_hash_map_iter_t ret = util_hash_map_iter_next(iter);
    util_pair_linked_list_t *bucket_list;
    bucket_list = iter.map->buckets[iter.bucket_num].bucket.bucket_list;
    iter.map->fun_destory_key(iter.bucket_list_node->first);
    iter.map->fun_destory_value(iter.bucket_list_node->second);
    util_linked_list_remove((util_linked_list_t*)bucket_list, (util_linked_list_node_t*)iter.bucket_list_node);
    iter.map->buckets[iter.bucket_num].size--;
    iter.map->size--;
    return ret;
}